package eu.irreality.dai.ui.keyboard;

import java.awt.event.KeyEvent;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.transform.TransformerException;

import eu.irreality.dai.gameplay.controller.actions.ControllerAction;
import eu.irreality.dai.gameplay.states.GameState;
import eu.irreality.dai.gameplay.states.GameStateManager;

/**
 * Singleton class that stores the mapping between keys and Actions. A key
 * performs an Action in a given GameState.
 * 
 */
public class InputManager
{
    /**
     * No modifier flag.
     */
    public final static int NONE = 0;
    /**
     * Shift modifier flag.
     */
    public final static int SHIFT = 1;
    /**
     * Alt modifier flag.
     */
    public final static int ALT = 2;
    /**
     * Control modifier flag.
     */
    public final static int CONTROL = 3;

    private final static int NUM_KEY_CODES = 600;
    private final static int NUM_MODIFIERS = 4;

    private static InputManager instance;

    /*
     * private Map[][] mapping;
     */

    private Map<KeyDescriptor, Map<Class<GameState>, Class<ControllerAction>>> mapping;

    private InputManager()
    {
	// mapping = new HashMap[NUM_MODIFIERS][NUM_KEY_CODES];
	mapping = new HashMap<KeyDescriptor, Map<Class<GameState>, Class<ControllerAction>>>();
	try
	{
	    List<ActionMapping> theMappings = ActionMapping.readMappings();
	    for (ActionMapping m : theMappings)
	    {
		mapAction(m);
	    }
	} catch (TransformerException e)
	{
	    e.printStackTrace();
	} catch (ClassNotFoundException e)
	{
	    e.printStackTrace();
	}
    }

    /**
     * Gets the singleton instance.
     * 
     * @return The singleton instance.
     */
    public static InputManager getInstance()
    {
	if (instance == null)
	{
	    instance = new InputManager();
	}
	return instance;
    }

    /**
     * Maps a given Action class to a given keystroke.
     * 
     * @param modifier
     *            One of the following NONE, SHIFT, ALT, CONTROL.
     * @param keyCode
     *            keyCode of the keystroke.
     * @param state
     *            State in which the mapping is aplicable.
     * @param actionClass
     *            Action class to be mapped.
     */
    /*
     * //obsolete public void mapAction(int modifier, int keyCode, GameState
     * state, Class actionClass) { if (mapping[modifier][keyCode] == null) {
     * mapping[modifier][keyCode] = new HashMap(); }
     * mapping[modifier][keyCode].put(state, actionClass); }
     */

    public void mapAction(KeyDescriptor desc, GameState state, Class actionClass)
    {
	if (mapping.get(desc) == null)
	{
	    mapping.put(desc,
		    new HashMap<Class<GameState>, Class<ControllerAction>>());
	}
	Class stateClass = state.getClass();
	mapping.get(desc).put(stateClass, actionClass);
    }

    /**
     * Maps an ActionFactory to a given keystroke using the information from an
     * ActionMapping.
     * 
     * @param am
     *            ActionMapping describing the mapping.
     * @throws ClassNotFoundException
     */
    public void mapAction(ActionMapping am) throws ClassNotFoundException
    {
	/*
	 * int modifier =
	 * translateModifiers(am.getKeyDescriptor().getModifiers());
	 * mapAction(modifier, am.getKeyDescriptor().getKeyCode(),
	 * GameStateManager .getState(am.getStateName()),
	 * Class.forName(am.getActionName()));
	 */
	mapAction(am.getKeyDescriptor(), GameStateManager.getState(am
		.getStateName()), Class.forName(am.getActionName()));
    }

    /**
     * Gets the Action class associated with a given KeyEvent in a given state.
     * 
     * @param e
     * @param currentState
     * @return
     */
    /*
     * public Class<Action> getKeyAction(KeyEvent e, GameState currentState) {
     * int modifiers = e.getModifiers(); int modifier =
     * translateModifiers(modifiers);
     * 
     * if (mapping[modifier][e.getKeyCode()] != null) { //
     * System.out.println("not null"); return (Class)
     * mapping[modifier][e.getKeyCode()].get(currentState); } else { return
     * null; } }
     */

    public Class<ControllerAction> getKeyAction(KeyDescriptor desc,
	    GameState currentState)
    {
	Map<Class<GameState>, Class<ControllerAction>> intermMap = null;
	if ((intermMap = mapping.get(desc)) != null)
	{
	    Class c = currentState.getClass();
	    return intermMap.get(c);
	} else
	{
	    return null;
	}
    }

    /**
     * Checks whether a given KeyEvent is mapped or not.
     * 
     * @param e
     *            KeyEvent to be checked.
     * @return true if the KeyEvent is mapped to some action.
     */
    /*
     * public boolean isMapped(KeyEvent e) { int modifier =
     * translateModifiers(e.getModifiers()); return
     * (mapping[modifier][e.getKeyCode()] != null); }
     */

    public boolean isMapped(KeyDescriptor kd)
    {
	return mapping.get(kd) != null;
    }

    private int translateModifiers(int modifiers)
    {
	int modifier = NONE;
	if ((modifiers & KeyEvent.SHIFT_DOWN_MASK) == KeyEvent.SHIFT_DOWN_MASK)
	{
	    modifier = SHIFT;
	} else if ((modifiers & KeyEvent.ALT_DOWN_MASK) == KeyEvent.ALT_DOWN_MASK)
	{
	    modifier = ALT;
	} else if ((modifiers & KeyEvent.CTRL_DOWN_MASK) == KeyEvent.CTRL_DOWN_MASK)
	{
	    modifier = CONTROL;
	}
	return modifier;
    }
}
